package com.xsw.common.utils;

import com.xsw.common.core.domain.TimeEntity;
import com.xsw.common.exception.ServiceException;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author yjc
 * @create 2023-03-07 15:06
 */
public class DateSelfUtils {

    /**
     * 时间格式(yyyy-MM-dd)
     */
    public final static String DATE_PATTERN = "yyyy-MM-dd";
    /**
     * 时间格式(yyyy-MM-dd HH:mm:ss)
     */
    public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    /**
     * 时间格式(yyyy-MM)
     */
    public final static String YEAR_MONTH_PATTERN = "yyyy-MM";
    /**
     * 时间格式(yyyy)
     */
    public final static String YEAR_PATTERN = "yyyy";
    /**
     * 时间格式(yyyy)
     */
    public final static String HOUR_PATTERN = "HH";
    /**
     * 时间格式(yyyyMMddHHmmss)
     */
    public final static String DATE_TOTAL_PATTERN = "yyyyMMddHHmmss";
    /**
     * 时间格式(yyyy年MM月dd日 HH:mm)
     */
    public final static String MINI_PUBACCOUNT_PATTERN = "yyyy年MM月dd日 HH:mm";
    /**
     * 时间格式(yyyy年MM月dd日)
     */
    public final static String MINI_PUBACCOUNT_SUB_PATTERN = "yyyy年MM月dd日";
    /**
     * 时间格式(HH:mm:ss)
     */
    public final static String TIME_PATTERN = "HH:mm:ss";

    /**
     * 日期格式化 日期格式为：yyyy-MM-dd
     *
     * @param date 日期
     * @return 返回yyyy-MM-dd格式日期
     */
    public static String format(Date date) {
        return format(date, DATE_PATTERN);
    }

    /**
     * 日期格式化 日期格式为：yyyy-MM-dd
     *
     * @param date    日期
     * @param pattern 格式，如：DateUtils.DATE_TIME_PATTERN
     * @return 返回yyyy-MM-dd格式日期
     */
    public static String format(Date date, String pattern) {
        if (date != null) {
            SimpleDateFormat df = new SimpleDateFormat(pattern);
            return df.format(date);
        }
        return null;
    }

    /**
     * 字符串转换成日期
     *
     * @param strDate 日期字符串
     * @param pattern 日期的格式，如：DateUtils.DATE_TIME_PATTERN
     */
    public static Date stringToDate(String strDate, String pattern) {
        if (StringUtils.isBlank(strDate)) {
            return null;
        }

        org.joda.time.format.DateTimeFormatter fmt = DateTimeFormat.forPattern(pattern);
        return fmt.parseLocalDateTime(strDate).toDate();
    }

    /**
     * 根据周数，获取开始日期、结束日期
     *
     * @param week 周期  0本周，-1上周，-2上上周，1下周，2下下周
     * @return 返回date[0]开始日期、date[1]结束日期
     */
    public static Date[] getWeekStartAndEnd(int week) {
        DateTime dateTime = new DateTime();
        org.joda.time.LocalDate date = new org.joda.time.LocalDate(dateTime.plusWeeks(week));

        date = date.dayOfWeek().withMinimumValue();
        Date beginDate = date.toDate();
        Date endDate = date.plusDays(6).toDate();
        return new Date[]{beginDate, endDate};
    }

    /**
     * 对日期的【秒】进行加/减
     *
     * @param date    日期
     * @param seconds 秒数，负数为减
     * @return 加/减几秒后的日期
     */
    public static Date addDateSeconds(Date date, int seconds) {
        DateTime dateTime = new DateTime(date);
        return dateTime.plusSeconds(seconds).toDate();
    }

    /**
     * 对日期的【分钟】进行加/减
     *
     * @param date    日期
     * @param minutes 分钟数，负数为减
     * @return 加/减几分钟后的日期
     */
    public static Date addDateMinutes(Date date, int minutes) {
        DateTime dateTime = new DateTime(date);
        return dateTime.plusMinutes(minutes).toDate();
    }

    /**
     * 对日期的【小时】进行加/减
     *
     * @param date  日期
     * @param hours 小时数，负数为减
     * @return 加/减几小时后的日期
     */
    public static Date addDateHours(Date date, int hours) {
        DateTime dateTime = new DateTime(date);
        return dateTime.plusHours(hours).toDate();
    }

    /**
     * 对日期的【天】进行加/减
     *
     * @param date 日期
     * @param days 天数，负数为减
     * @return 加/减几天后的日期
     */
    public static Date addDateDays(Date date, int days) {
        DateTime dateTime = new DateTime(date);
        return dateTime.plusDays(days).toDate();
    }

    /**
     * 对日期的【周】进行加/减
     *
     * @param date  日期
     * @param weeks 周数，负数为减
     * @return 加/减几周后的日期
     */
    public static Date addDateWeeks(Date date, int weeks) {
        DateTime dateTime = new DateTime(date);
        return dateTime.plusWeeks(weeks).toDate();
    }

    /**
     * 对日期的【月】进行加/减
     *
     * @param date   日期
     * @param months 月数，负数为减
     * @return 加/减几月后的日期
     */
    public static Date addDateMonths(Date date, int months) {
        DateTime dateTime = new DateTime(date);
        return dateTime.plusMonths(months).toDate();
    }

    /**
     * 对日期的【年】进行加/减
     *
     * @param date  日期
     * @param years 年数，负数为减
     * @return 加/减几年后的日期
     */
    public static Date addDateYears(Date date, int years) {
        DateTime dateTime = new DateTime(date);
        return dateTime.plusYears(years).toDate();
    }


    /**
     * 判断目前登录时间与指定的日期是否是同一天
     *
     * @param specifiedDate 指定的日期，格式为xxxx-xx-xx
     * @return {@link null}
     * @author wusihao
     * @date 2023/8/17 10:50
     */
    public static boolean isSameDayAsSpecifiedDate(String specifiedDate) {
        // 获取当前日期
        LocalDate currentDate = LocalDate.now();

        // 将指定的周期时间字符串解析为LocalDate对象
        LocalDate parsedDate = LocalDate.parse(specifiedDate, DateTimeFormatter.ISO_DATE);

        // 判断是否是同一天
        boolean isSameDay = currentDate.isEqual(parsedDate);

        return isSameDay;
    }

    /**
     * 获取指定日期的开始日期（即当天的 00:00:00）
     *
     * @param date 指定日期
     * @return 开始日期
     */
    public static Calendar getStartDate(Date date) {
        Calendar start = Calendar.getInstance();
        start.setTime(date);
        start.set(Calendar.HOUR_OF_DAY, 0);
        start.set(Calendar.MINUTE, 0);
        start.set(Calendar.SECOND, 0);
        return start;
    }

    /**
     * 获取指定日期的结束日期（即当天的 23:59:59）
     *
     * @param date 指定日期
     * @return 结束日期
     */
    public static Calendar getEndDate(Date date) {
        Calendar end = Calendar.getInstance();
        end.setTime(date);
        end.set(Calendar.HOUR_OF_DAY, 23);
        end.set(Calendar.MINUTE, 59);
        end.set(Calendar.SECOND, 59);
        return end;
    }


    /**
     * 获取两个日期之间所有日期，格式为（yyyy-MM）
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static List<Date> getMonthBetweenMonths(Date startDate, Date endDate) {
        Calendar start = Calendar.getInstance();
        Calendar end = Calendar.getInstance();

        //设置开始和结束月份
        start.setTime(startDate);
        end.setTime(endDate);

        List<Date> months = getMonthsBetween(start, end);

//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");

        return months;

    }

    public static List<Date> getMonthsBetween(Calendar start, Calendar end) {
        List<Date> results = new ArrayList<>();
        Calendar temp = (Calendar) start.clone();
        temp.set(Calendar.DAY_OF_MONTH, 1);
        while (temp.before(end)) {
            results.add(temp.getTime());
            temp.add(Calendar.MONTH, 1);
        }
        results.add(end.getTime());
        return results;
    }


    /**
     * 获取两个日期之间的所有日期
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static List<Date> getDatesBetweenUsingJava8(LocalDate startDate, LocalDate endDate) {
        long numOfDaysBetween = ChronoUnit.DAYS.between(startDate, endDate);
        return IntStream.iterate(0, i -> i + 1)
                .limit(numOfDaysBetween)
                .mapToObj(i -> changeLocalDateToDate(startDate.plusDays(i)))
                .collect(Collectors.toList());
    }


    /**
     * 获取 获取某年某月 所有日期（yyyy-mm-dd格式字符串）
     *
     * @param month
     * @return
     */
    public static List<Date> getMonthFullDay(Date month) {
        List<Date> fullDayList = new ArrayList<>(32);
        // 获得当前日期对象
        Calendar cal = Calendar.getInstance();
        cal.clear();// 清除信息

        Date startDate = getFirstDayOfMonth(month);
        // 结束日期为当前年拼接12月份
        Date endDate = getLastDayOfMonth(month);
        // 设置calendar的开始日期
        cal.setTime(startDate);
        // 当前时间小于等于设定的结束时间
        while (cal.getTime().compareTo(endDate) <= 0) {
            fullDayList.add(cal.getTime());
            // 当前天数
            cal.add(Calendar.DAY_OF_MONTH, 1);
        }
        return fullDayList;
    }


    /**
     * 获取指定年份1月到12月的日期
     *
     * @param yearDate 传值模板：2024
     * @return
     */
    public static List<Date> getMonthByYear(String yearDate) {
        List<Date> data = new ArrayList<>();
        try {
            Calendar c = Calendar.getInstance();
            // 获取当前的年份
//            int year = c.get(Calendar.YEAR);
            // 定义时间格式
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
            // 开始日期为当前年拼接1月份
            Date startDate = sdf.parse(yearDate + "-01");
            // 结束日期为当前年拼接12月份
            Date endDate = sdf.parse(yearDate + "-12");
            // 设置calendar的开始日期
            c.setTime(startDate);
            // 当前时间小于等于设定的结束时间
            while (c.getTime().compareTo(endDate) <= 0) {
                data.add(c.getTime());
                // 当前月份加1
                c.add(Calendar.MONTH, 1);
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return data;
    }


    /**
     * 获取两个日期相隔天数/小时数/分钟数
     *
     * @return
     * @throws ParseException
     */
    public static TimeEntity getBetweenTime(String s1, String s2) throws ParseException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        String s1 = "2023-03-02 07:54:11";
//        String s2 = "2023-03-02 08:54:11";
        Date date1 = format.parse(s1);
        Date date2 = format.parse(s2);
        boolean sameDay = DateUtils.isSameDay(date1, date2);
        Integer days = null;
        Integer hours = null;
        Integer minutes = null;
        if (sameDay) {//同一天
            minutes = Math.abs(minuteBetween(date1, date2));
            minutes = minutes >= 60 ? null : minutes;
            hours = (minutes == null || minutes >= 60) ? Math.abs(daysBetween(date1, date2, 1)) : null;
        } else {//不同天
            format = new SimpleDateFormat("yyyy-MM-dd");
            date1 = format.parse(s1);
            date2 = format.parse(s2);
            days = daysBetween(date1, date2, 24);
        }

//        log.info("sameDay:{}", sameDay);
//        log.info("days:{}", days);
//        log.info("hours:{}", hours);
//        log.info("minute:{}", minutes);
        TimeEntity timeVo = new TimeEntity();
        timeVo.setDays(days);
        timeVo.setHours(hours);
        timeVo.setMinutes(minutes);
        return timeVo;
    }

    /**
     * JAVA计算两个日期相差多少天和小时(by date)
     *
     * @author zhengkai.blog.csdn.net
     */
    public static int daysBetween(Date date1, Date date2, Integer isDay) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date1);
        long time1 = cal.getTimeInMillis();
        cal.setTime(date2);
        long time2 = cal.getTimeInMillis();
        long between_days = (time2 - time1) / (1000 * 3600 * isDay);
        return Integer.parseInt(String.valueOf(between_days));
    }

    /**
     * JAVA计算两个日期相差多少天和小时(by date)
     *
     * @author zhengkai.blog.csdn.net
     */
    public static int minuteBetween(Date date1, Date date2) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date1);
        long time1 = cal.getTimeInMillis();
        cal.setTime(date2);
        long time2 = cal.getTimeInMillis();
        long between_days = (time2 - time1) / (1000 * 60 * 1);
        return Integer.parseInt(String.valueOf(between_days));
    }


    /**
     * 获取指定年的第一天
     */
    public static Date getCurrentFirstOfYear(Calendar currCal) {
        int currentYear = currCal.get(Calendar.YEAR);
        return getFirstOfYear(currentYear);
    }

    /**
     * 获取指定年的最后一天
     */
    public static Date getCurrentLastOfYear(Calendar currCal) {
        int currentYear = currCal.get(Calendar.YEAR);
        return getLastOfYear(currentYear);
    }

    /**
     * 获取某年第一天日期
     *
     * @param year 年份
     * @return Date
     */
    public static Date getFirstOfYear(int year) {
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.set(Calendar.YEAR, year);
        return calendar.getTime();
    }


    /**
     * 获取某年最后一天日期
     *
     * @param year 年份
     * @return Date
     */
    public static Date getLastOfYear(int year) {
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.set(Calendar.YEAR, year);
        calendar.roll(Calendar.DAY_OF_YEAR, -1);
        return calendar.getTime();
    }


    /**
     * 获取当月第一天日期
     *
     * @param currentDate 当天日期
     * @return
     */
    public static Date getFirstDayOfMonth(Date currentDate) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(currentDate);
        calendar.add(Calendar.MONTH, 0);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        String firstDay = sdf.format(calendar.getTime());
//        log.info("firstDay:{}", firstDay);
        return stringToDate(firstDay, DATE_TIME_PATTERN);

    }

    /**
     * 获取当月最后一天日期
     *
     * @param currentDate 当天日期
     * @return
     */
    public static Date getLastDayOfMonth(Date currentDate) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(currentDate);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        String lastDay = sdf.format(calendar.getTime());
        return stringToDate(lastDay, DATE_TIME_PATTERN);

    }

    /**
     * 每周的所有日期  即周一到周日
     *
     * @param currentDate 当前日期
     * @return {@link List <LocalDate>}
     */
    public static List<LocalDate> allDaysOfWeek(LocalDate currentDate) {
        List<LocalDate> allDays = new ArrayList<>();
        Date date = new Date();
        allDays.add(currentDate.with(DayOfWeek.MONDAY));
        allDays.add(currentDate.with(DayOfWeek.TUESDAY));
        allDays.add(currentDate.with(DayOfWeek.WEDNESDAY));
        allDays.add(currentDate.with(DayOfWeek.THURSDAY));
        allDays.add(currentDate.with(DayOfWeek.FRIDAY));
        allDays.add(currentDate.with(DayOfWeek.SATURDAY));
        allDays.add(currentDate.with(DayOfWeek.SUNDAY));
        return allDays;
    }

    /**
     * 根据日期取得星期几
     *
     * @param date
     * @return
     */
    public static String getWeek(Date date) {
        String[] weeks = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int week_index = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (week_index < 0) {
            week_index = 0;
        }
        return weeks[week_index];
    }

    /**
     * 根据日期取得星期几
     *
     * @param date
     * @return
     */
    public static String getWeekDay(Date date) {
        String[] weeks = {"周一", "周二", "周三", "周四", "周五", "周六", "周日"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int week_index = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (week_index < 0) {
            week_index = 0;
        }
        return weeks[week_index];
    }

    /**
     * 根据日期取得星期几(0表示星期日，1表示星期一，以此类推)
     * <p>
     * 0、1、2、3、4、5、6
     * 周日, 周一, 周二, 周三, 周四, 周五, 周六
     *
     * @param date 指定的时间
     * @return {@link null}
     * @author wusihao
     * @date 2023/8/17 11:00
     */

    public static Integer getWeekNumber(Date date) {
        String[] weeks = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int week_index = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (week_index < 0) {
            week_index = 0;
        }
        return week_index;
    }

    /**
     * 将LocalDate转换成Date
     *
     * @param localDate
     */
    public static Date changeLocalDateToDate(LocalDate localDate) {
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdt = localDate.atStartOfDay(zoneId);

        Date date = Date.from(zdt.toInstant());
        return date;
    }

    /**
     * 将Date转换成LocalDate
     *
     * @param startTime
     */
    public static LocalDate changeDateToLocalDate(Date startTime) {
        Instant instant = startTime.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();

        // atZone()方法返回在指定时区从此Instant生成的ZonedDateTime。
        LocalDate localDate = instant.atZone(zoneId).toLocalDate();
        return localDate;
    }

    /**
     * 获取指定时间范围内所有星期一的日期
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static List<Date> getAllMondays(Date startDate, Date endDate) {
        LocalDate startLocalDate = changeDateToLocalDate(startDate);
        LocalDate endLocalDate = changeDateToLocalDate(endDate);


        List<Date> mondays = new ArrayList<>();
        LocalDate currentMonday = startLocalDate.with(DayOfWeek.MONDAY);

        // 处理起始日期本身就是周一的情况
        if (currentMonday.isBefore(startLocalDate)) {
            currentMonday = currentMonday.plusWeeks(1);
        }

        while (!currentMonday.isAfter(endLocalDate)) {
            mondays.add(changeLocalDateToDate(currentMonday));
            currentMonday = currentMonday.plusWeeks(1);
        }
        return mondays;
    }

    /**
     * 获取指定日期范围内所有月份的第一天日期
     *
     * @param startDate
     * @param endDate
     * @return
     * @throws Exception
     */
    public static List<Date> getFirstDaysOfMonths(Date startDate, Date endDate) {

        List<Date> firstDays = new ArrayList<>();
        try {
            Calendar calStart = Calendar.getInstance();
            calStart.setTime(startDate);
            calStart.set(Calendar.DAY_OF_MONTH, 1);

            Calendar calEnd = Calendar.getInstance();
            calEnd.setTime(endDate);
            calEnd.set(Calendar.DAY_OF_MONTH, 1);

            while (!calStart.after(calEnd)) {
                firstDays.add(calStart.getTime());
                calStart.add(Calendar.MONTH, 1);
            }
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }

        return firstDays;
    }

    /**
     * 将秒数转化成 时-分-秒格式
     *
     * @param totalSeconds
     * @return
     */
    public static String convertSeconds(long totalSeconds) {
        long hours = TimeUnit.SECONDS.toHours(totalSeconds);
        long minutes = TimeUnit.SECONDS.toMinutes(totalSeconds) % 60;
        long seconds = totalSeconds % 60;
        return String.format("%02d-%02d-%02d", hours, minutes, seconds);
    }
}
